package com.belazy.library.util.rsa;

import sun.misc.BASE64Decoder;
import sun.misc.BASE64Encoder;

import javax.crypto.Cipher;
import java.security.*;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;

/**
 * @Description <p> Rsa加密算法 </p>
 * @Author chupeng.tang
 * @CreateTime 2023/5/19 17:29
 * @Version 1.0
 */
public class RsaUtil {
    private static PrivateKey PRIVATE_KEY;
    private static PublicKey PUBLIC_KEY;
    private static String PRI_KEY = "";
    private static String PUB_KEY = "";

    static {
        try {
            PRIVATE_KEY = getPrivateKey (PRI_KEY);
            PUBLIC_KEY = getPublicKey (PUB_KEY);
        } catch (Exception e) {
            PRIVATE_KEY = null;
            PUBLIC_KEY = null;
        }
    }

    /**
     * <p>生成RSA密钥对(默认密钥长度为1024)</p>
     *
     * @return {@link KeyPair}
     * @auhor chupeng.tang
     * @datetime 2023/5/19 18:13
     **/
    public static KeyPair generateRSAKeyPair() throws NoSuchAlgorithmException {
        return generateRSAKeyPair (1024);
    }

    /**
     * <p>生成RSA密钥对</p>
     *
     * @return {@link KeyPair}
     * @auhor chupeng.tang
     * @datetime 2023/5/19 18:13
     * @param: keyLength  密钥长度
     **/
    public static KeyPair generateRSAKeyPair(int keyLength) throws NoSuchAlgorithmException {
        KeyPairGenerator kpg = KeyPairGenerator.getInstance ("RSA");
        kpg.initialize (keyLength);
        return kpg.genKeyPair ();
    }


    /**
     * <p>用公钥加密</p>
     *
     * @return {@link String} 加密密文字符串
     * @auhor chupeng.tang
     * @datetime 2023/5/19 18:11
     * @param: str 待加密字符串
     * @param: enCode 字符串编码格式
     * @param: pubKey  字符串编码格式
     **/
    public static String encryptData(String str, String enCode, PublicKey pubKey) {
        try {
            byte[] data = str.getBytes (enCode);
            Cipher cipher = Cipher.getInstance ("RSA/ECB/PKCS1Padding");
            cipher.init (Cipher.ENCRYPT_MODE, pubKey);
            return (new BASE64Encoder ()).encode (cipher.doFinal (data));
        } catch (Exception e) {
            throw new RuntimeException (e);
        }
    }

    /**
     * <p>使用既有的公钥加密</p>
     *
     * @return {@link String} 加密密文字符串
     * @auhor chupeng.tang
     * @datetime 2023/5/19 18:11
     * @param: encryptedStr  待加密字符串
     **/
    public static String encryptData(String encryptedStr) {
        String enCode = "UTF-8";
        return encryptData (encryptedStr, enCode, PUBLIC_KEY);
    }


    /**
     * <p>用私钥解密</p>
     *
     * @return {@link String} 解密明文字符串
     * @auhor chupeng.tang
     * @datetime 2023/5/19 18:11
     * @param: encryptedStr 待解密字符串
     * @param: enCode 字符串编码格式
     * @param: priKey 私钥
     **/
    public static String decryptData(String encryptedStr, String enCode, PrivateKey priKey) {
        try {
            byte[] encryptedData = (new BASE64Decoder ()).decodeBuffer (encryptedStr);
            Cipher cipher = Cipher.getInstance ("RSA");
            cipher.init (Cipher.DECRYPT_MODE, priKey);
            return new String (cipher.doFinal (encryptedData), enCode);
        } catch (Exception e) {
            throw new RuntimeException (e);
        }
    }

    /**
     * <p>使用既有的私钥进行解密</p>
     *
     * @return {@link String} 解密明文字符串
     * @auhor chupeng.tang
     * @datetime 2023/5/19 18:10
     * @param: encryptedStr  待解密字符串
     **/
    public static String decryptData(String encryptedStr) {
        String enCode = "UTF-8";
        return decryptData (encryptedStr, enCode, PRIVATE_KEY);
    }

    /**
     * <p>根据指定私钥对数据进行签名(默认签名算法为"SHA1withRSA")</p>
     *
     * @return {@link byte[]} 签名串字节数组
     * @auhor chupeng.tang
     * @datetime 2023/5/19 18:08
     * @param: data 要签名的数据字节数组
     * @param: priKey  私钥
     **/
    public static byte[] signData(byte[] data, PrivateKey priKey) {
        return signData (data, priKey, "SHA1withRSA");
    }

    /**
     * <p>根据指定私钥和算法对数据进行签名</p>
     *
     * @return {@link byte[]} 签名串字节数组
     * @auhor chupeng.tang
     * @datetime 2023/5/19 18:08
     * @param: data 要签名的数据字节数组
     * @param: priKey  私钥
     * @param: algorithm  签名算法
     **/
    public static byte[] signData(byte[] data, PrivateKey priKey, String algorithm) {
        try {
            Signature signature = Signature.getInstance (algorithm);
            signature.initSign (priKey);
            signature.update (data);
            return signature.sign ();
        } catch (Exception ex) {
            return null;
        }
    }


    /**
     * <p>验证签名算法,默认使用："SHA1withRSA"签名算法</p>
     *
     * @return {@link boolean} 返回结果
     * @auhor chupeng.tang
     * @datetime 2023/5/19 18:07
     * @param: data 数据字节数组
     * @param: sign 签名字节数组
     * @param: pubKey 公钥
     **/
    public static boolean verifySign(byte[] data, byte[] sign, PublicKey pubKey) {
        return verifySign (data, sign, pubKey, "SHA1withRSA");
    }

    /**
     * <p>验证签名算法</p>
     *
     * @return {@link boolean} 返回结果
     * @auhor chupeng.tang
     * @datetime 2023/5/19 18:06
     * @param: data 数据字节数组
     * @param: sign 签名字节数组
     * @param: pubKey 公钥
     * @param: algorithm  签名算法
     **/
    public static boolean verifySign(byte[] data, byte[] sign, PublicKey pubKey, String algorithm) {
        try {
            Signature signature = Signature.getInstance (algorithm);
            signature.initVerify (pubKey);
            signature.update (data);
            return signature.verify (sign);
        } catch (Exception ex) {
            return false;
        }
    }

    /**
     * <p>从base64字符串获取公钥</p>
     *
     * @return {@link PublicKey} 公钥
     * @auhor chupeng.tang
     * @datetime 2023/5/19 18:04
     * @param: key
     * @throw Exception
     **/
    public static PublicKey getPublicKey(String key) throws Exception {
        byte[] keyBytes;
        keyBytes = (new BASE64Decoder ()).decodeBuffer (key);

        X509EncodedKeySpec keySpec = new X509EncodedKeySpec (keyBytes);
        KeyFactory keyFactory = KeyFactory.getInstance ("RSA");
        PublicKey publicKey = keyFactory.generatePublic (keySpec);
        return publicKey;
    }

    /**
     * <p>从base64字符串获取私钥</p>
     *
     * @return {@link PrivateKey} 私钥
     * @auhor chupeng.tang
     * @datetime 2023/5/19 18:03
     * @param: key
     **/
    public static PrivateKey getPrivateKey(String key) throws Exception {
        byte[] keyBytes;
        keyBytes = (new BASE64Decoder ()).decodeBuffer (key);
        PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec (keyBytes);
        KeyFactory keyFactory = KeyFactory.getInstance ("RSA");
        PrivateKey privateKey = keyFactory.generatePrivate (keySpec);
        return privateKey;
    }

    /**
     * <p>把公、密钥转成base64字符串</p>
     *
     * @return {@link String} base64字符串
     * @auhor chupeng.tang
     * @datetime 2023/5/19 18:02
     * @param: key
     **/
    public static String getKeyString(Key key) throws Exception {
        byte[] keyBytes = key.getEncoded ();
        String s = (new BASE64Encoder ()).encode (keyBytes);
        return s;
    }

    public static void main(String[] args) throws Exception {
//        KeyPair keyPair = generateRSAKeyPair (1024);
//        String preKey = (new BASE64Encoder ()).encode (keyPair.getPrivate ().getEncoded ());
//        String pubKey = (new BASE64Encoder ()).encode (keyPair.getPublic ().getEncoded ());
//        System.out.println ("密钥："+preKey);
//        System.out.println ("公钥："+pubKey);

//        String a = encryptData ("1234567");
//        String b = decryptData (a);
//        System.out.println ("加密串:" + a);
//        System.out.println ("明文串:" + b);
        System.out.println (PUB_KEY);

    }
}
